package org.example.ams.controller.system.workflow;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.example.ams.entity.system.workflow.FlowModel;
import org.example.common.api.CommonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 流程模型管理
 * @author cheval
 */
@Api(tags = "流程模型管理")
@RestController
@RequestMapping("/flow/model")
public class FlowModelController {

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ObjectMapper objectMapper;

    @ApiOperation("查询流程模型")
    @GetMapping("/list")
    public CommonResult<Map<String,Object>> list(@RequestParam(required = false) String key,
                             @RequestParam(required = false) String name,
                             @RequestParam(defaultValue = "1") Integer currentPage,
                             @RequestParam(defaultValue = "10") Integer pageSize) {
        ModelQuery modelQuery = repositoryService.createModelQuery();
        if (StringUtils.isNotEmpty(key)) {
            modelQuery.modelKey(key);
        }
        if (StringUtils.isNotEmpty(name)) {
            modelQuery.modelName(name);
        }
        List<Model> models = modelQuery.orderByCreateTime().desc().listPage(currentPage, pageSize);
        Map<String,Object> res = new HashMap<>();
        res.put("total",models.size());
        res.put("data",models);
        return CommonResult.success(res,null);
    }

    @ApiOperation("检查模型key是否已经存在,存在返回true,不存在返回false")
    @GetMapping("/checkKey")
    public CommonResult checkModelKeyIsExist(String key) {
        ModelQuery modelQuery = repositoryService.createModelQuery();
        List<Model> list = modelQuery.modelKey(key).list();
        if (list != null && list.size() != 0) {
            return CommonResult.success(true,null);
        }
        return CommonResult.success(false,null);
    }

    @ApiOperation("添加流程模型")
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("/add")
    public CommonResult add(FlowModel flowModel) {
        // 1.保存模型到act_re_model表
        Model model = repositoryService.newModel();
        model.setKey(flowModel.getKey());
        model.setName(flowModel.getName());
        model.setCategory(flowModel.getCategory());
        model.setVersion(1);
        ObjectNode metaInfo = objectMapper.createObjectNode();
        metaInfo.put(ModelDataJsonConstants.MODEL_NAME,flowModel.getName())
                .put(ModelDataJsonConstants.MODEL_DESCRIPTION,flowModel.getDescription())
                .put(ModelDataJsonConstants.MODEL_REVISION,1);
        model.setMetaInfo(metaInfo.toString());
        repositoryService.saveModel(model);
        // 2. 保存模型文件到act_ge_bytearray
        Map<String,Object> content = new HashMap<>();
        Map<String,String> properties = new HashMap<>();
        properties.put("process_id",model.getKey());
        properties.put("name",model.getName());
        properties.put("category",model.getCategory());
        Map<String,String> stencilset = new HashMap<>();
        stencilset.put("namespace","http://b3mn.org/stencilset/bpmn2.0#");

        content.put("resourceId",model.getId());
        content.put("properties",properties);
        content.put("stencilset",stencilset);
        try {
            repositoryService.addModelEditorSource(model.getId(),objectMapper.writeValueAsBytes(content));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return CommonResult.success(null);
    }

    @Transactional(rollbackFor = Exception.class)
    @ApiOperation("模型部署")
    @PostMapping("/deploy")
    public CommonResult deploy(String id) {
        Model model = repositoryService.getModel(id);
        try {
            JsonNode node = objectMapper.readTree(repositoryService.getModelEditorSource(id));
            BpmnModel bpmnModel = (new BpmnJsonConverter()).convertToBpmnModel(node);
            Deployment deployment = repositoryService.createDeployment()
                    .category(model.getCategory()).name(model.getName()).key(model.getKey())
                    .addBpmnModel(model.getKey() + ".bpmn20.xml", bpmnModel).deploy();
            model.setDeploymentId(deployment.getId());
            repositoryService.saveModel(model);
            return CommonResult.success(null);
        } catch (IOException e) {
            e.printStackTrace();
            return CommonResult.failed("流程图不合规范，请重新设计！");
        }
    }

    @ApiOperation("删除模型")
    @PostMapping("/remove")
    public CommonResult remove(String id) {
        repositoryService.deleteModel(id);
        return CommonResult.success(null);
    }

    @ApiOperation("导出流程模型图")
    @GetMapping("/export")
    public void modelExport(String id, HttpServletResponse response) throws IOException {
        byte[] modelData = repositoryService.getModelEditorSource(id);
        JsonNode jsonNode = objectMapper.readTree(modelData);
        BpmnModel bpmnModel = (new BpmnJsonConverter()).convertToBpmnModel(jsonNode);
        byte[] xmlBytes = (new BpmnXMLConverter()).convertToXML(bpmnModel, "UTF-8");
        ByteArrayInputStream in = new ByteArrayInputStream(xmlBytes);
        IOUtils.copy(in, response.getOutputStream());
        String filename = bpmnModel.getMainProcess().getId() + ".bpmn20.xml";
        response.setHeader("Content-Disposition","attachment;filename=" + filename);
        response.setHeader("content-Type", "application/xml");
        response.flushBuffer();
    }
}
